Board
OLEM is provided with 2 types of boards : the first type is made up of squares and the second one is made up of triangles forming hexagonal shapes.
To help with using those boards in game, the board module provides the data structures and API that we use for our own games. There is also a level editor that can be used to create levels through a GUI.
To introduce the board API and data structures, we will start with the smallest units and move our way up to the entire board.
The boards and their components
Elements
An element is used to represent something that resides on the board. It could be:
- OLEM
- A physical object (game piece), such as a wall, a miniature, resources
- Data tied to a specific position that won’t be seen by users but can be used by the game code. For example, in Guardian & Thieves, possible treasure spawning positions are saved in the level but actual treasures are generated by code when loading the level.
Data structure
An element is an object that has:
- A type stored as a string
- Customizable parameters, those have :
- a string name (key)
- a string value associated to that string (think Hashmap / JSON)
API
"""
Class constructor
:param type_string: string type
"""
Element(type_string)
"""
Returns the element's type param. Gives the same output as get_param("type").
"""
get_type()
"""
Get the value of the given parameter
:param param_name: string name of the parameter to be retrieved
Returns the associated string value
"""
get_param(param_name)
"""
Set the value of a parameter. If a parameter with the same name already
exists, value is overriden, otherwise it is created.
:param param_name: string name of the parameter
:param value: string value associated to the name
"""
set_param(param_name, value)
Containers
To place elements on the board, they are stored in containers. There are 3 types of containers :
- Node
- Cell
- Border
Those types are explained in more details in the Tiles section below
Data structure
A container is a list of elements.
API
"""
Check if the container contains at least one element of
a specific type
:param type: string type to check
Returns a boolean
"""
has_element_type(type)
"""
Add an element to the container.
:param element: Element object to be stored in container
"""
add_element(element)
"""
Remove element from container. This element is NOT destroyed.
:param element: element obj to be removed from container
"""
remove_element(element)
"""
Remove all elements from the container.
"""
clear_elements()
"""
Returns the list of elements in the container.
"""
get_elements()
"""
Get the first element of the specified type.
:param string_type: string type to search for
Returns an Element object
"""
get_element_by_type(string_type)
"""
Get all elements of the specified type.
:param string_type: string type to search for
Returns a list of Element objects
"""
get_elements_by_type(string_type)
"""
Checks if the container has elements stored.
Returns a boolean
"""
is_empty()
Tiles
Tiles are the basic units that are repeated in order to create a board. They look a bit different depending on the type of board but they still follow a common structure created using the containers introduced above :
- Nodes are positions between cells on the board, where OLEM can be (other elements can be placed there). There is only one node per tile
- Cells are, well, traditional cells on a board game
- Borders are the areas between cells, where, for example, walls can be placed
Data structure
A tile contains :
- its (x, y) coordinates on the board
- One node
- An array of cells
- An array of borders
Square tile
For the board made up of squares, a tile is defined as follow:
with:
- 1, the node
- 2, the cell (here there is only one)
- 3, the top border
- 4, the left border
Triangular tiles
with:
- 1 the node
- 2 the cells
- 3 the borders
API
"""
Get the border of a tile
:param id: in a squareboard 0 is the top horizontal border, 1 is the left vertical border
Returns a container
"""
get_border(id)
"""
Get the cell of a tile
Returns a container
"""
get_cell()
"""
Get the node of a tile
Returns a container
"""
get_node()
"""
Get x coordinate of a tile
"""
get_x()
"""
Get y coordinate of a tile
"""
get_y()
"""
Checks if the cells, node and borders are all empty
Returns a boolean
"""
is_empty()
Board
We define a board as an object that has:
- a number of columns
- a number of rows
- tiles of a specific dimensions (width and height, both in mm)
- a name
- an array of customizable parameters
- an array of tiles
By positioning several tiles next to each other, we end up with this structure (screenshoted from level editor):
NOTE: With the structure we use some of the containers are actually outside the board as some nodes are on the edge of the board
"""
Create an empty squareboard
NB: see genericboard.load_board() to load a board created with the level editor
:param rows: number of rows
:param columns: number of columns
:param tile_width: width of a tile in mm
:param tile_height: height of a tile in mm
Returns a squareboard object
"""
squareboard(rows, columns, tile_width, tile_height)
"""
Displays all tiles of the board with a single digit per tile.
0 indicates an empty tile, 1 indicates not empty
"""
print()
"""
Destroy the board object
NB: Right now this has to be called manually to avoid memory leak.
We are looking for a proper way to automatically destroy it as this is
an issue when the script crashes.
"""
__del__()
"""
Get the number of rows in the board
Returns an int
"""
get_row_count()
"""
Get the number of columns in the board
Returns an int
"""
get_column_count()
"""
Check if a position is valid on the board
:param x: x coordinate on the board
:param y: y coordinate on the board
Return a boolean
"""
is_position_within_board(x, y)
"""
Get tile from its position
:param x: x coordinate on the board
:param y: y coordinate on the board
Return a tile object or None if tile does not exists
"""
get_tile(x, y)
"""
Get level name
Returns a string
"""
get_name()
"""
Get the value of the given parameter
:param param_name: string name of the parameter to be retrieved
Returns the associated string value
"""
get_param(param_name)
"""
Set the value of a parameter. If a parameter with the same name already
exists, value is overriden, otherwise it is created.
:param param_name: string name of the parameter
:param value: string value associated to the name
"""
set_param(param_name, value)
########################################
# Not implemetend or not tested
get_tile_dimension
get_tiles
get_neighbor_tiles
get_border_from_tile(tile, direction)
get_border_from_position(x, y, direction)
Static functions
Those functions are not called on the objects
"""
Creates a board based on a json file generated by the level editor
:param path_to_file: path in filesystem to board
Returns a squareboard object
"""
genericboard.load_board(path_to_file)
Example
Basic
import genericboard
import olem
TYPE_START = "start"
TYPE_WALL = "wall"
TYPE_END = "end"
def find_starting_pos(b):
for y in range(b.get_row_count()):
for x in range(b.get_column_count()):
tile = b.get_tile(x, y)
node = tile.get_node()
if (node.has_element_type(TYPE_START)):
print("found start at " + str(x) + " " + str(y))
return (x, y)
def is_node_walkable(board, x, y):
board_edge = False
if not board.is_position_within_board(x, y):
board_edge = True
if not board.is_position_within_board(x - 1, y):
board_edge = True
if not board.is_position_within_board(x, y - 1):
board_edge = True
if board_edge:
print("board edge is not walkable")
return False
has_wall = False
tile = board.get_tile(x, y)
if tile.get_node().has_element_type(TYPE_WALL):
has_wall = True
if tile.get_border(0).has_element_type(TYPE_WALL):
has_wall = True
if tile.get_border(1).has_element_type(TYPE_WALL):
has_wall = True
if(has_wall):
print("target tile has walls")
return False
print("looking left")
tile = board.get_tile(x - 1, y)
if tile.get_border(0).has_element_type(TYPE_WALL):
return False
print("looking up")
tile = board.get_tile(x, y - 1)
if tile.get_border(1).has_element_type(TYPE_WALL):
return False
return True
def get_command():
msg = olem.gmsg_wait_for_message()
if msg == "up":
return (0, -1)
if msg == "down":
return (0, 1)
if msg == "left":
return (-1, 0)
if msg == "right":
return (1, 0)
return (0, 0)
def end_reached(board, current_position):
tile = board.get_tile(current_position[0], current_position[1])
return tile.get_node().has_element_type(TYPE_END)
print("Board tester")
b = genericboard.load_board("/tmp/board_tester")
b.print()
starting_position = find_starting_pos(b)
current_position = starting_position
while not end_reached(b, current_position):
command = get_command()
if is_node_walkable(b, current_position[0] + command[0], current_position[1] + command[1]):
current_position = (current_position[0] + command[0], current_position[1] + command[1])
print("Moved to " + str(current_position[0]) + " " + str(current_position[1]))
else:
print("Node " + str(current_position[0] + command[0]) + " " + str(current_position[1] + command[1]) + " is not walkable")
print("End reached")
b.__del__()